package main

import (
	"fmt"
	"math"
)

/*
接口类型 是由一组方法签名定义的集合。
接口类型的变量可以保存任何实现了这些方法的值。
*/
// 定义接口
type Phone interface {
	call()
}

// 下面是实现接口的方法
type NokiaPhone struct {
}

func (nokiaphone NokiaPhone) call() {
	fmt.Printf("I am Nokia \n")
}

// 这也是实现接口的方法之一
type IPhone struct {
}

func (iPhone IPhone) call() {
	fmt.Printf("I am iPhone \n")
}

type Abser interface {
	Abs() float64 // 后面一个参数表示返回值类型
}

func main() {
	// 这是将函数当做方法使用；
	new_nokia := NokiaPhone{}
	new_nokia.call()
	// 下面是使用接口
	// 先声明一个接口变量
	var n_phone Phone
	// 然后将实现方法的接收者传给这个接口变量
	n_phone = new(NokiaPhone)
	// 然后这个接口变量就可以实现接口内的方法
	n_phone.call()

	//定义一个接口变量，这个接口变量可以包含任意实现它的方法；所有空接口可以包含任何变量
	// 在这里IPhone接口体实现里接口里面的call方法，因此这里的i_iphone接口变量可以被赋值为IPone{}这个结构体
	var i_phone Phone
	i_phone = IPhone{}
	i_phone.call()
	i_phone = &IPhone{} // 也可以包含指针类型的变量
	i_phone.call()

	var a Abser
	var b Abser
	f := MyFloat(-3.256)
	v := Vertex{3, 4}

	a = f
	v.Abs() //这是可以的
	b = &v  // 这个不行；若按照之前的规则，以指针作为接收者的方法是可以使用值类型调用的，
	// 在这里不行的原因是使用接口后，方法要求的是指针类型，因此必须将接口传如指针类型，或者认为接口没有实现值类型的方法。
	fmt.Println(a.Abs())
	fmt.Println(b.Abs())
	// 什么是空接口：指定了零个方法的接口值被称为 *空接口：*interface{}
	var new_i interface{}
	describe(new_i)
	new_i = 42
	describe(new_i)
	new_i = "hello"
	describe(new_i)
	// 空接口可保存任何类型的值。（因为每个类型都至少实现了零个方法。）空接口被用来处理未知类型的值

	// 类型断言 提供了访问接口值底层具体值的方式。t := i.(T)
	var i interface{} = "hello" // 为什么这样是可行的；因为空接口可以保存任何类型的值

	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	fl, ok := i.(float64)
	fmt.Println(fl, ok)

	// fl = i.(float64) // 这样会发生错误
	// fmt.Println(fl)

	// mytype := i.(type)  //这样也会报错，要想这样获取类型，必须配合switch使用
	// fmt.Println(mytype)
	do("hello")
	do(25)
	do(true)
	// Stringer 是一个可以用字符串描述自己的类型。fmt 包（还有很多包）都通过此接口来打印值。
	person1 := Person{Name: "Bop"}
	person2 := Person{Name: "jack", Age: 25}
	fmt.Println(person1)
	fmt.Println(person2)
}

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func do(i interface{}) {
	switch mytype := i.(type) {
	case int:
		fmt.Printf("Twice %v is %v\n", mytype, mytype*2)
	case string:
		fmt.Printf("%q is %d bytes long\n", mytype, len(mytype))
	default:
		fmt.Printf("I don't know about type %T!\n", mytype)
	}
}
func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
	if f < 0 {
		return float64(-f)
	}
	return float64(f)
}

type Vertex struct {
	X, Y float64
}

func (v *Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
